﻿// ----------------------------------------------------------------------------
// Copyright (C) 2009 XXXXX
// 版权所有。 
//
// 文件名：DataAccessAdapter.cs
// 文件功能描述：DataAccessAdapter Class 基于DataSet的读写数据库的基类，基于POCO的读写数据库的基类请查询
// Husb 写的 DataAccessObjectBase 类
// 
// 创建标识：2009-3-9 9:31:44 husb    Original Version
//
// 修改标识：2009-3-9
// 修改描述：
//
//
// 注：2007-05-11 husb 此类由于没有优化，里面有一些重复的代码，且功能不完善，已经过时，请使用husb关于此功能的新类
// ----------------------------------------------------------------------------


using System;
using System.Collections.Generic;

using System.Text;
using System.Data.Common;
using Husb.DataUtil;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Husb.Util;
using System.Collections.Specialized;

namespace Husb.Data
{
    public delegate void PopulateParameters(Database db, DbCommand cmd);
    public delegate void PopulateCustomParameters(Database db, DbCommand cmd, params object[] objects);
    public delegate void PopulateIdParameters(Database db, DbCommand cmd, Guid id);
    public delegate void PopulateStringIdParameters(Database db, DbCommand cmd, string id);
    public abstract class DataAccessAdapter<S, T, R>
        where S : DataSet, new()
        where T : DataTable, new()
        where R : DataRow
    {

        #region Fields
        private Database db;

        private DbDataAdapter adapter;
        private string tableName = "";
        private string masterTableName;
        private string detailTableName;
        private string detailTableName2;
        private string detailTableName3;
        private string masterFieldName;
        private DbCommand selectCommand;

        private DbCommand insertCommand = null;
        private DbCommand updateCommand = null;
        private DbCommand deleteCommand = null;

        public string GetByIdStoredProc;
        public string GetByNameStoredProc;

        public string GetAllStoredProc;
        public string InsertStoredProc;
        public string UpdateStoredProc;
        public string DeleteStoredProc;

        public string GetRowCountStoredProc;
        public string GetPagedStoredProc;
        public string GetDynamicStoredProc;

        public string GetByMasterIdStoredProc;
        public string FindStoredProc;

        public string LogicalDeleteStoredProc;
        #endregion

        #region constructor
        /// <summary>
        /// 如果操作的DataSet含有多个DataTable,请对TableName赋值
        /// </summary>
        public DataAccessAdapter()
        {
            db = DatabaseFactory.CreateDatabase();
            adapter = db.GetDataAdapter();
        }
        #endregion

        #region Properties
        /// <summary>
        /// 一般来说是对应的数据库中的Table、View，也可以是自定义DataTable名，要视情况而定
        /// </summary>
        public string TableName
        {
            get 
            {
                return this.tableName;
            }
            set { this.tableName = value; }
        }

        /// <summary>
        /// 主表名，当需要操作当前表的主表时需要设置此项
        /// </summary>
        public string MasterTableName
        {
            get { return this.masterTableName; }
            set { this.masterTableName = value; }
        }
        /// <summary>
        /// DataTable的外键，当需要操作当前表的主表时需要设置此项
        /// </summary>
        public string MasterFieldName
        {
            get { return this.masterFieldName; }
            set { this.masterFieldName = value; }
        }
        /// <summary>
        /// 从表名，当需要操作当前表的从表时需要设置此项
        /// </summary>
        public string DetailTableName
        {
            get { return this.detailTableName; }
            set { this.detailTableName = value; }
        }
        /// <summary>
        /// 从表名，当需要操作当前表的从表时需要设置此项
        /// </summary>
        public string DetailTableName2
        {
            get { return this.detailTableName2; }
            set { this.detailTableName2 = value; }
        }
        /// <summary>
        /// 从表名，当需要操作当前表的从表时需要设置此项
        /// </summary>
        public string DetailTableName3
        {
            get { return this.detailTableName3; }
            set { this.detailTableName3 = value; }
        }


        private static T Table(S dataSet)
        {
            T tbl = null;
            foreach (DataTable t in dataSet.Tables)
            {
                if (t is T)
                {
                    tbl = t as T;
                    break;
                }
            }
            return tbl;

        }
        #endregion

        #region Reject
        public DbDataAdapter Adapter
        {
            get
            {
                if ((this.adapter == null))
                {
                    this.InitAdapter();
                }
                return this.adapter;
            }
        }

        private void InitAdapter()
        {
            if (this.adapter == null)
            {
                this.adapter = db.GetDataAdapter();

            }
            System.Data.Common.DataTableMapping tableMapping = new System.Data.Common.DataTableMapping();

            tableMapping.SourceTable = "Table";
            //tableMapping.DataSetTable = "Departments";

            //CreateDataTableMapping(tableMapping); 

            //tableMapping.ColumnMappings.Add("Id", "Id");
            //tableMapping.ColumnMappings.Add("Name", "Name");
            //tableMapping.ColumnMappings.Add("ParentId", "ParentId");
            //tableMapping.ColumnMappings.Add("Category", "Category");
            //tableMapping.ColumnMappings.Add("DepartmentNumber", "DepartmentNumber");
            //tableMapping.ColumnMappings.Add("QueryNumber", "QueryNumber");
            //tableMapping.ColumnMappings.Add("IsDeleted", "IsDeleted");
            //tableMapping.ColumnMappings.Add("IsActive", "IsActive");
            //tableMapping.ColumnMappings.Add("CreatedTime", "CreatedTime");
            //tableMapping.ColumnMappings.Add("CreatedBy", "CreatedBy");
            //tableMapping.ColumnMappings.Add("ModifiedTime", "ModifiedTime");
            //tableMapping.ColumnMappings.Add("LastModifiedBy", "LastModifiedBy");
            //tableMapping.ColumnMappings.Add("Version", "Version");
            //tableMapping.ColumnMappings.Add("Description", "Description");

            this.adapter.TableMappings.Add(tableMapping);


        }

        //protected abstract void CreateDataTableMapping(System.Data.Common.DataTableMapping tableMapping);

        #endregion

        #region PrepareCommand

        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommand(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();

            selectCommand = CreateCommand(selectCommand, commandText, isStoredProc);
            //DbCommand cmd = isStoredProc ? db.GetStoredProcCommand(commandText) : cmd = db.GetSqlStringCommand(commandText);

            if (parameters != null)
            {
                DataAccessUtil.PopulateParameters(parameters, db, selectCommand);
            }

            return selectCommand;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommandFromDataSet(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();

            selectCommand = CreateCommand(selectCommand, commandText, isStoredProc);
            //DbCommand cmd = isStoredProc ? db.GetStoredProcCommand(commandText) : cmd = db.GetSqlStringCommand(commandText);

            if (parameters != null)
            {
                DataAccessUtil.PopulateParametersFromDataSet(parameters, db, selectCommand);
            }

            return selectCommand;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommand(string commandText, PopulateParameters populateParameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            selectCommand = CreateCommand(selectCommand, commandText, isStoredProc);

            //DbCommand cmd = isStoredProc ? db.GetStoredProcCommand(commandText) : cmd = db.GetSqlStringCommand(commandText);
            if (populateParameters != null)
            {
                populateParameters(db, selectCommand);
            }

            return selectCommand;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommand(DbCommand cmd, string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            cmd = CreateCommand(cmd, commandText, isStoredProc);

            //DbCommand cmd = isStoredProc ? db.GetStoredProcCommand(commandText) : cmd = db.GetSqlStringCommand(commandText);

            if (parameters != null)
            {
                DataAccessUtil.PopulateParameters(parameters, db, cmd);
            }

            return cmd;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommand(DbCommand cmd, string commandText, PopulateParameters populateParameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            cmd = CreateCommand(cmd, commandText, isStoredProc);
            if (populateParameters != null)
            {
                populateParameters(db, cmd);
            }

            return cmd;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommand(DbCommand cmd, string commandText, PopulateCustomParameters populateParameters, bool isStoredProc, params object[] objects)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            cmd = CreateCommand(cmd, commandText, isStoredProc);
            if (populateParameters != null)
            {
                populateParameters(db, cmd, objects);
            }

            return cmd;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareCommand(string commandText, PopulateCustomParameters populateParameters, bool isStoredProc, params object[] objects)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            selectCommand = CreateCommand(selectCommand, commandText, isStoredProc);

            //DbCommand cmd = isStoredProc ? db.GetStoredProcCommand(commandText) : cmd = db.GetSqlStringCommand(commandText);
            if (populateParameters != null)
            {
                populateParameters(db, selectCommand, objects);
            }

            return selectCommand;
        }
        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareDeleteCommand(DbCommand cmd, string commandText, Guid id, PopulateIdParameters populateIdParameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            cmd = CreateCommand(cmd, commandText, isStoredProc);
            if (populateIdParameters != null)
            {
                populateIdParameters(db, cmd, id);
            }

            return cmd;
        }

        protected DbCommand PrepareDeleteCommand(DbCommand cmd, string commandText, string id, PopulateStringIdParameters populateIdParameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            cmd = CreateCommand(cmd, commandText, isStoredProc);
            if (populateIdParameters != null)
            {
                populateIdParameters(db, cmd, id);
            }

            return cmd;
        }

        /// <summary>
        /// 准备命令对象
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        protected DbCommand PrepareDeleteCommand(DbCommand cmd, string commandText, int id, IEnumerable<DatabaseParameter> parameters, bool isStoredProc)
        {
            if (db == null) db = DatabaseFactory.CreateDatabase();
            cmd = CreateCommand(cmd, commandText, isStoredProc);
            if (parameters != null)
            {
                DataAccessUtil.PopulateParameters(parameters, db, cmd);
            }

            return cmd;
        }

        private DbCommand CreateCommand(DbCommand cmd, string commandText, bool isStoredProc)
        {
            //if (cmd != null)
            //{
            //    cmd.Parameters.Clear();
            //    cmd.CommandText = commandText;
            //    cmd.CommandType = CommandType.StoredProcedure;
            //}
            //else
            //{
            //    cmd = db.GetStoredProcCommand(commandText);
            //}

            cmd = isStoredProc ? db.GetStoredProcCommand(commandText) : cmd = db.GetSqlStringCommand(commandText);

            if (!isStoredProc)
            {
                cmd.CommandType = CommandType.Text;
            }

            return cmd;
        }

        #endregion

        #region GetDataSet & GetTable
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public virtual T GetTable(string commandText, IEnumerable<DatabaseParameter> parameters)
        {
            S dataSet = this.GetDataSet(commandText, parameters, true, true, null);
            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, this.TableName);

            return this.tableName.Length > 0 ? (T)dataSet.Tables[tableName] : (T)dataSet.Tables[0];
            //return dataSet;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual T GetTable(string commandText, IEnumerable<DatabaseParameter> parameters, bool enableCache)
        {

            S dataSet = this.GetDataSet(commandText, parameters, true, enableCache, null);
            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, this.TableName);

            return this.tableName.Length > 0 ? (T)dataSet.Tables[tableName] : (T)dataSet.Tables[0]; //(T)dataSet.Tables[0];
            //return dataSet;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <returns></returns>
        public virtual T GetTable(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, bool enableCache)
        {

            S dataSet = this.GetDataSet(commandText, parameters, isStoredProc, enableCache, null);
            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, this.TableName);

            return this.tableName.Length > 0 ? (T)dataSet.Tables[tableName] : (T)dataSet.Tables[0]; //(T)dataSet.Tables[0];
            //return dataSet;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        public virtual T GetTable(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, bool enableCache, string cacheKey)
        {

            S dataSet = this.GetDataSet(commandText, parameters, isStoredProc, enableCache, cacheKey);
            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, this.TableName);

            return this.tableName.Length > 0 ? (T)dataSet.Tables[tableName] : (T)dataSet.Tables[0]; //(T)dataSet.Tables[0];
            //return dataSet;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="populateParametersHandler"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <param name="objects"></param>
        /// <returns></returns>
        public virtual T GetTable(string commandText, PopulateCustomParameters populateParametersHandler, bool isStoredProc, bool enableCache, string cacheKey, params object[] objects)
        {

            S dataSet = this.GetDataSet(commandText, populateParametersHandler, isStoredProc, enableCache, cacheKey, objects);
            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, this.TableName);

            return this.tableName.Length > 0 ? (T)dataSet.Tables[tableName] : (T)dataSet.Tables[0]; ;// (T)dataSet.Tables[0];
            //return dataSet;
        }



        /// <summary>
        /// 根据sql语句、sp、参数返回DataSet。需要设定是否从缓存中读取
        /// 这是其他GetDataSet调用的方法
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        public virtual S GetDataSet(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, bool enableCache, string cacheKey)
        {
            #region Pull Cache
            S dataSet = null;
            ICacheManager cacheManager = null;
            string key = "";
            
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                else
                {
                    if (!isStoredProc)
                    {
                        key = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
                    }
                    else
                    {
                        key = DataAccessUtil.GenerateCacheKey(commandText, parameters);
                    }
                }
                cacheManager = CacheFactory.GetCacheManager();
                dataSet = cacheManager.GetData(key) as S;
                if (dataSet != null)
                {
                    return dataSet;
                }
            }
            #endregion

            #region LoadDataSetFromDB
            dataSet = new S();

            //string[] tableNames = new string[dataSet.Tables.Count];
            //for (int i = 0; i < dataSet.Tables.Count; i++)
            //{
            //    tableNames[i] = dataSet.Tables[i].TableName;
            //}

            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, tableNames);
            try
            {
                db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName);//
            }
            catch
            {
                throw;
            }
            #endregion

            #region Push Cache
            if (enableCache && cacheManager != null)
            {
                if ((this.tableName.Length > 0 ? dataSet.Tables[tableName] : dataSet.Tables[0]).Rows.Count > 0)
                {
                    DataAccessUtil.InsertCache(key, dataSet, cacheManager);
                }
            }
            #endregion

            return dataSet;
        }

        /// <summary>
        /// 可以指定是否缓存当前的DataSet，命令名为存储过程名， 缓存Key根据存储过程名自动生成
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="enableCache"></param>
        /// <returns></returns>
        public virtual S GetDataSet(string commandText, IEnumerable<DatabaseParameter> parameters, bool enableCache)
        {
            return this.GetDataSet(commandText, parameters, true, enableCache, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="populateParametersHandler"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <param name="objects"></param>
        /// <returns></returns>
        public virtual S GetDataSet(string commandText, PopulateCustomParameters populateParametersHandler, bool isStoredProc, bool enableCache, string cacheKey, params object[] objects)
        {
            #region Pull Cache
            S dataSet = null;
            ICacheManager cacheManager = null;
            string key = "";
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                else
                {
                    //key = DataAccessUtil.GenerateCacheKey(commandText, objects);
                    if (!isStoredProc)
                    {
                        key = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
                    }
                    else
                    {
                        key = DataAccessUtil.GenerateCacheKey(commandText, objects);
                    }
                }
                cacheManager = CacheFactory.GetCacheManager();
                dataSet = cacheManager.GetData(key) as S;
                if (dataSet != null)
                {
                    return dataSet;
                }
            }
            #endregion

            #region LoadDataSetFromDB
            dataSet = new S();
            //string[] tableNames = new string[dataSet.Tables.Count];
            //for (int i = 0; i < dataSet.Tables.Count; i++)
            //{
            //    tableNames[i] = dataSet.Tables[i].TableName;
            //}

            //db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, tableNames);
            db.LoadDataSet(PrepareCommand(commandText, populateParametersHandler, isStoredProc, objects), dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName);
            #endregion

            #region Push Cache
            if (enableCache && cacheManager != null)
            {
                if ((this.tableName.Length > 0 ? dataSet.Tables[tableName] : dataSet.Tables[0]).Rows.Count > 0)
                {
                    DataAccessUtil.InsertCache(key, dataSet, cacheManager);
                }
            }
            #endregion

            return dataSet;
        }

        #endregion

        #region Fill
        /// <summary>
        /// 填充已有的DataSet
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="dataSet"></param>
        /// <param name="tableName"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        public void Fill(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, S dataSet, string tableName, bool enableCache, string cacheKey)
        {
            #region Cache
            ICacheManager cacheManager = null;
            string key = commandText;
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                else
                {
                    key = DataAccessUtil.GenerateCacheKey(commandText, parameters);
                }
                cacheManager = CacheFactory.GetCacheManager();
                S ds = cacheManager.GetData(key) as S;

                if (ds != null)
                {
                    DataTable dtCache = this.tableName.Length > 0 ? ds.Tables[tableName] : ds.Tables[0];
                    DataTable dtDest = this.tableName.Length > 0 ? dataSet.Tables[tableName] : dataSet.Tables[0];

                    foreach (DataRow row in dtCache.Rows)
                    {
                        dtDest.ImportRow(row);
                    }
                    //dataSet = ds.Copy() as S;
                    return;
                }
            }
            #endregion

            db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, tableName);

            #region Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessUtil.InsertCache(key, dataSet, cacheManager);
            }
            #endregion
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="dataSet"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        public void Fill(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, S dataSet, bool enableCache, string cacheKey)
        {
            Fill(commandText, parameters, isStoredProc, dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName, enableCache, cacheKey);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="dataSet"></param>
        /// <param name="enableCache"></param>
        public void Fill(string commandText, IEnumerable<DatabaseParameter> parameters, S dataSet, bool enableCache)
        {
            Fill(commandText, parameters, true, dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName, enableCache, null);
        }

        //public void Fill(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, S dataSet, string tableName)
        //{
        //    Fill(commandText, parameters, isStoredProc, dataSet, tableName, false, null);
        //}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="dataSet"></param>
        /// <param name="tableNames"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        public void Fill(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, S dataSet, string[] tableNames, bool enableCache, string cacheKey)
        {
            #region Cache
            ICacheManager cacheManager = null;
            string key = commandText;
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                else
                {
                    key = DataAccessUtil.GenerateCacheKey(commandText, parameters);
                }
                cacheManager = CacheFactory.GetCacheManager();
                S ds = cacheManager.GetData(key) as S;

                if (ds != null)
                {
                    //dataSet = ds.Copy() as S;
                    DataTable dtCache = this.tableName.Length > 0 ? ds.Tables[tableName] : ds.Tables[0];
                    DataTable dtDest = this.tableName.Length > 0 ? dataSet.Tables[tableName] : dataSet.Tables[0];

                    foreach (DataRow row in dtCache.Rows)
                    {
                        dtDest.ImportRow(row);
                    }
                    //dataSet.Tables[0] = ds.Tables[0];
                    return;
                }
            }
            #endregion

            db.LoadDataSet(PrepareCommand(commandText, parameters, isStoredProc), dataSet, tableNames);

            #region Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessUtil.InsertCache(key, dataSet, cacheManager);
            }
            #endregion
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ds"></param>
        /// <param name="tableName"></param>
        /// <param name="enableCache"></param>
        public void Fill(S ds, string tableName, bool enableCache)
        {
            if (ds.Tables.Count > 0)
            {
                Fill(GetAllStoredProc, null, true, ds, tableName, enableCache, null);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ds"></param>
        /// <param name="tableName"></param>
        public void Fill(S ds, string tableName)
        {
            if (ds.Tables.Count > 0)
            {
                Fill(GetAllStoredProc, null, true, ds, tableName, false, null);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ds"></param>
        /// <param name="enableCache"></param>
        public void Fill(S ds, bool enableCache)
        {
            if (ds.Tables.Count > 0)
            {
                Fill(GetAllStoredProc, null, true, ds, this.tableName.Length > 0 ? this.tableName : ds.Tables[0].TableName, enableCache, null);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ds"></param>
        public void Fill(S ds)
        {
            if (ds.Tables.Count > 0)
            {
                Fill(GetAllStoredProc, null, true, ds, this.tableName.Length > 0 ? this.tableName : ds.Tables[0].TableName, false, null);
            }
        }
        #endregion

        #region Get
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual T GetAll()
        {
            return this.GetTable(GetAllStoredProc, null, true);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        public virtual T GetAll(bool isStoredProc, bool enableCache, string cacheKey)
        {
            return this.GetTable(GetAllStoredProc, new List<DatabaseParameter>(), isStoredProc, enableCache, cacheKey);
        }

        public virtual R GetById(string id, bool isStoredProc)
        {
            if (String.IsNullOrEmpty(GetByIdStoredProc))
            {
                throw new ArgumentNullException(GetByIdStoredProc);
            }
            List<DatabaseParameter> ps = new List<DatabaseParameter>();
            ps.Add(new DatabaseParameter("Id", DbType.String, id));

            T t = GetTable(GetByIdStoredProc, ps, isStoredProc, false);
            return t.Rows.Count > 0 ? (R)t.Rows[0] : null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual R GetById(Guid id, bool isStoredProc)
        {
            if (String.IsNullOrEmpty(GetByIdStoredProc))
            {
                throw new ArgumentNullException(GetByIdStoredProc);
            }
            List<DatabaseParameter> ps = new List<DatabaseParameter>();
            ps.Add(new DatabaseParameter("Id", DbType.Guid, id));

            T t = GetTable(GetByIdStoredProc, ps, isStoredProc, false);
            return t.Rows.Count > 0 ? (R)t.Rows[0] : null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="entityId"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual R GetById(int entityId, bool isStoredProc)
        {
            if (String.IsNullOrEmpty(GetByIdStoredProc))
            {
                throw new ArgumentNullException(GetByIdStoredProc);
            }
            List<DatabaseParameter> ps = new List<DatabaseParameter>();
            ps.Add(new DatabaseParameter("Id", DbType.Int32, entityId));

            T t = GetTable(GetByIdStoredProc, ps, isStoredProc, false);
            return t.Rows.Count > 0 ? (R)t.Rows[0] : null;
        }
        /// <summary>
        /// 为了性能原因，可以从缓存中读取相应的对象
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual R GetByIdFromCache(Guid id)
        {
            T dataTable = this.GetAll();


            //T retVal = new T();
            //foreach (R row in dataTable.Rows)
            //{
            //    if (row[""] == id)
            //    {
            //        retVal.ImportRow(row);
            //        break;
            //    }
            //}
            //return retVal.Rows.Count > 0 ? (R)retVal.Rows[0] : null;

            return dataTable.Rows.Find(id) as R;

            //DataView dv = new DataView(dataTable, "Id = '" + id.ToString() + "'", null, DataViewRowState.CurrentRows);
            //return dv.Count > 0 ? (R)dv[0].Row : null;
        }

        public virtual R GetByIdFromCache(Guid id, bool isStoredProc)
        {

            T dataTable = this.GetAll(isStoredProc, true, null);
            if (dataTable.PrimaryKey == null)
            {
                throw new Exception("您没有给数据集的表：" + dataTable.TableName + "设置主键列。");
            }
            return dataTable.Rows.Find(id) as R;
        }

        public virtual R GetByIdFromCache(string id, bool isStoredProc)
        {
            T dataTable = this.GetAll(isStoredProc, true, null);
            if (dataTable.PrimaryKey == null)
            {
                throw new Exception("您没有给数据集的表：" + dataTable.TableName + "设置主键列。");
            }
            return dataTable.Rows.Find(id) as R;
        }

        public virtual R GetByIdFromCache(string id)
        {
            T dataTable = this.GetAll();


            //T retVal = new T();
            //foreach (R row in dataTable.Rows)
            //{
            //    if (row[""] == id)
            //    {
            //        retVal.ImportRow(row);
            //        break;
            //    }
            //}
            //return retVal.Rows.Count > 0 ? (R)retVal.Rows[0] : null;

            return dataTable.Rows.Find(id) as R;

            //DataView dv = new DataView(dataTable, "Id = '" + id.ToString() + "'", null, DataViewRowState.CurrentRows);
            //return dv.Count > 0 ? (R)dv[0].Row : null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual T GetByName(string name, bool exactMatch, bool isStoredProc, bool enableCache)
        {
            if (String.IsNullOrEmpty(GetByNameStoredProc))
            {
                throw new ArgumentNullException(GetByNameStoredProc);
            }
            List<DatabaseParameter> ps = new List<DatabaseParameter>();
            ps.Add(new DatabaseParameter("Name", DbType.String, name));
            ps.Add(new DatabaseParameter("ExactMatch", DbType.Boolean, exactMatch));

            return GetTable(GetByNameStoredProc, ps, isStoredProc, enableCache);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="masterId"></param>
        /// <param name="masterFieldName"></param>
        /// <returns></returns>
        public virtual T GetByMasterId(Guid masterId, string masterFieldName)
        {
            if (String.IsNullOrEmpty(GetByMasterIdStoredProc))
            {
                throw new ArgumentNullException(GetByMasterIdStoredProc + "没有赋值，需要在Adapter类中为其赋值");
            }
            List<DatabaseParameter> ps = new List<DatabaseParameter>();
            ps.Add(new DatabaseParameter(masterFieldName, DbType.Guid, masterId));

            return GetTable(GetByMasterIdStoredProc, ps, true, false);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="masterId"></param>
        /// <returns></returns>
        public virtual T GetByMasterId(Guid masterId)
        {
            if (String.IsNullOrEmpty(MasterFieldName))
            {
                throw new ArgumentNullException(MasterFieldName + "没有赋值，需要在Adapter类中为其赋值");
            }

            return GetByMasterId(masterId, this.MasterFieldName);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual R[] GetByNameFromCache(string name)
        {
            T dataTable = this.GetAll();

            return dataTable.Select("Name LIKE '%" + name + "%'") as R[];
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual R GetByNameExactMatchFromCache(string name)
        {
            T dataTable = this.GetAll();

            R[] rows = dataTable.Select("Name = '" + name + "'") as R[];
            if (rows != null && rows.Length > 0)
            {
                return rows[0];
            }
            return null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="whereClause"></param>
        /// <param name="orderByExpression"></param>
        /// <param name="hideDeleted"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <returns></returns>
        public virtual T Find(string whereClause, string orderByExpression, bool hideDeleted, bool isStoredProc, bool enableCache)
        {
            if (String.IsNullOrEmpty(FindStoredProc))
            {
                throw new ArgumentNullException("FindStoredProc不能为空, 请Adapter中设置FindStoredProc的值.");
            }

            if (hideDeleted)
            {
                whereClause = DataAccessUtil.AppendDeletedWhereClause(whereClause);
            }

            IEnumerable<DatabaseParameter> ps = GetSearchParameters(whereClause, orderByExpression);

            return GetTable(FindStoredProc, ps, isStoredProc, enableCache, FindStoredProc + DataAccessUtil.GetKeyStringByParamters(ps));
        }

        #endregion

        #region paging
        /// <summary>
        /// 
        /// </summary>
        /// <param name="startRowIndex"></param>
        /// <param name="maximumRows"></param>
        /// <param name="sortExpression"></param>
        /// <param name="whereClause"></param>
        /// <param name="orderBy"></param>
        /// <param name="hideDeleted"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <returns></returns>
        public virtual T GetPaged(int startRowIndex, int maximumRows, string sortExpression, string whereClause, string orderBy, bool hideDeleted, bool isStoredProc, bool enableCache)
        {
            if (string.IsNullOrEmpty(GetPagedStoredProc))
            {
                throw new Exception("自定义分页的存储过程必须存在，并且要在子类中赋值!");
            }

            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            parameters.Add(new DatabaseParameter("startRowIndex", DbType.Int32, startRowIndex));
            parameters.Add(new DatabaseParameter("maximumRows", DbType.Int32, maximumRows));
            parameters.Add(new DatabaseParameter("sortExpression", DbType.String, sortExpression));

            if (hideDeleted)
            {
                whereClause = DataAccessUtil.AppendDeletedWhereClause(whereClause);
            }
            IEnumerable<DatabaseParameter> ps = String.IsNullOrEmpty(whereClause) ? GetSearchParameters(orderBy) : GetSearchParameters(whereClause, orderBy);
            foreach (DatabaseParameter p in ps)
            {
                parameters.Add(p);
            }

            T t = GetTable(GetPagedStoredProc, parameters, isStoredProc, enableCache, GetPagedStoredProc + DataAccessUtil.GetKeyStringByParamters(parameters));
            //return GetTable(GetPagedStoredProc, parameters, isStoredProc, enableCache, GetPagedStoredProc + DataAccessUtil.GetKeyStringByParamters(parameters));

            return t;

            //return GetObjectList(GetPagedSortedProc, parameters, enableCache, GetPagedSortedProc + DataAccessHelper.GetKeyStringByParamters(parameters));

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="whereClause"></param>
        /// <param name="hideDeleted"></param>
        /// <param name="enableCache"></param>
        /// <returns></returns>
        public virtual int GetCount(string whereClause, bool hideDeleted, bool enableCache)
        {
            if (string.IsNullOrEmpty(GetRowCountStoredProc))
            {
                throw new Exception("获取记录数的存储过程必须存在，并且要在子类中赋值!");
            }

            int recordCount = 0;
            if (hideDeleted)
            {
                whereClause = DataAccessUtil.AppendDeletedWhereClause(whereClause);
            }
            //object o = GetValue(GetRowCountStoredProc, parameters, enableCache, GetRowCountStoredProc + whereClause);
            object o = GetValue(GetRowCountStoredProc, GetSearchParameters(whereClause, null), true, enableCache, GetRowCountStoredProc + whereClause);

            if (o != null)
            {
                recordCount = (int)o;
            }

            return recordCount;
        }
        #endregion

        #region GetValue
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        public virtual object GetValue(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, bool enableCache, string cacheKey)
        {
            #region Pull Cache
            object o = null;
            ICacheManager cacheManager = null;
            string key = "";
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                else
                {
                    key = DataAccessUtil.GenerateCacheKey(commandText, parameters);
                }
                cacheManager = CacheFactory.GetCacheManager();
                o = cacheManager.GetData(key);
                if (o != null)
                {
                    return o;
                }
            }
            #endregion

            o = db.ExecuteScalar(PrepareCommand(commandText, parameters, isStoredProc));

            #region Push Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessUtil.InsertCache(key, o, cacheManager);
            }
            #endregion

            return o;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual object GetValue(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc)
        {
            return GetValue(commandText, parameters, isStoredProc, false, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <returns></returns>
        public virtual object GetValue(string commandText)
        {
            return this.GetValue(commandText, new List<DatabaseParameter>(), true);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="populateParametersHandler"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="enableCache"></param>
        /// <param name="cacheKey"></param>
        /// <param name="objects"></param>
        /// <returns></returns>
        public virtual object GetValue(string commandText, PopulateCustomParameters populateParametersHandler, bool isStoredProc, bool enableCache, string cacheKey, params object[] objects)
        {
            #region Pull Cache
            object o = null;
            ICacheManager cacheManager = null;
            string key = "";
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                else
                {
                    key = DataAccessUtil.GenerateCacheKey(commandText, objects);
                }
                cacheManager = CacheFactory.GetCacheManager();
                o = cacheManager.GetData(key);
                if (o != null)
                {
                    return o;
                }
            }
            #endregion

            o = db.ExecuteScalar(PrepareCommand(commandText,  populateParametersHandler, isStoredProc, objects));

            #region Push Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessUtil.InsertCache(key, o, cacheManager);
            }
            #endregion

            return o;
        }


        #endregion

        #region ExecuteNonQuery
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <returns></returns>
        public virtual int ExecuteNonQuery(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, bool removeCache)
        {
            int cnt = -1;

            cnt = db.ExecuteNonQuery(PrepareCommand(commandText, parameters, isStoredProc));
            if (cnt > 0 && removeCache)
            {
                DataAccessUtil.UpdateCacheDependency(commandText);
            }
            return cnt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual int ExecuteNonQuery(string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc)
        {
            int cnt = -1;

            cnt = db.ExecuteNonQuery(PrepareCommand(commandText, parameters, isStoredProc));
            if (cnt > 0)
            {
                DataAccessUtil.UpdateCacheDependency(commandText);
            }
            return cnt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="populateParametersHandler"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual int ExecuteNonQuery(string commandText, PopulateParameters populateParametersHandler, bool isStoredProc)
        {
            int cnt = -1;

            cnt = db.ExecuteNonQuery(PrepareCommand(commandText, populateParametersHandler, isStoredProc));
            if (cnt > 0)
            {
                DataAccessUtil.UpdateCacheDependency(commandText);
            }
            return cnt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText1"></param>
        /// <param name="populateParametersHandler1"></param>
        /// <param name="commandText2"></param>
        /// <param name="populateParametersHandler2"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public virtual bool ExecuteNonQuery(string commandText1, PopulateParameters populateParametersHandler1, string commandText2, PopulateParameters populateParametersHandler2, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
        {
            bool returnValue = false;

            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    db.ExecuteNonQuery(PrepareCommand(null, commandText1, new PopulateParameters(populateParametersHandler1), isStoredProc), transaction);
                    db.ExecuteNonQuery(PrepareCommand(null, commandText2, new PopulateParameters(populateParametersHandler2), isStoredProc), transaction);

                    transaction.Commit();

                    if (removeCache && cacheItemKeys != null)
                    {
                        DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                    }
                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText1"></param>
        /// <param name="parameters1"></param>
        /// <param name="commandText2"></param>
        /// <param name="parameters2"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public virtual bool ExecuteNonQuery(string commandText1, IEnumerable<DatabaseParameter> parameters1, string commandText2, IEnumerable<DatabaseParameter> parameters2, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
        {
            bool returnValue = false;

            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    db.ExecuteNonQuery(PrepareCommand(null, commandText1, parameters1, isStoredProc), transaction);
                    db.ExecuteNonQuery(PrepareCommand(null, commandText2, parameters2, isStoredProc), transaction);

                    transaction.Commit();

                    if (removeCache && cacheItemKeys != null)
                    {
                        DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                    }
                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TAdapter1"></typeparam>
        /// <typeparam name="OS1"></typeparam>
        /// <typeparam name="OT1"></typeparam>
        /// <typeparam name="OR1"></typeparam>
        /// <typeparam name="TAdapter2"></typeparam>
        /// <typeparam name="OS2"></typeparam>
        /// <typeparam name="OT2"></typeparam>
        /// <typeparam name="OR2"></typeparam>
        /// <param name="commandText"></param>
        /// <param name="populateParametersHandler"></param>
        /// <param name="detailDataSet1"></param>
        /// <param name="detailDataSet2"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public virtual bool ExecuteNonQuery<TAdapter1, OS1, OT1, OR1, TAdapter2, OS2, OT2, OR2>(string commandText, PopulateParameters populateParametersHandler, DataSet detailDataSet1, DataSet detailDataSet2, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
            where TAdapter1 : DataAccessAdapter<OS1, OT1, OR1>, new()
            where TAdapter2 : DataAccessAdapter<OS2, OT2, OR2>, new()
            where OS1 : DataSet, new()
            where OT1 : DataTable, new()
            where OR1 : DataRow
            where OS2 : DataSet, new()
            where OT2 : DataTable, new()
            where OR2 : DataRow
        {
            bool returnValue = false;
            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    db.ExecuteNonQuery(PrepareCommand(null, commandText, new PopulateParameters(populateParametersHandler), isStoredProc), transaction);

                    TAdapter1 detailAdapter1 = new TAdapter1();
                    db.UpdateDataSet(detailDataSet1, String.IsNullOrEmpty(this.detailTableName) ? detailDataSet1.Tables[0].TableName : this.detailTableName,
                        detailAdapter1.PrepareCommand(null, detailAdapter1.InsertStoredProc, new PopulateParameters(detailAdapter1.PopulateInsertParameters), isStoredProc),
                        detailAdapter1.PrepareCommand(null, detailAdapter1.UpdateStoredProc, new PopulateParameters(detailAdapter1.PopulateUpdateParameters), isStoredProc),
                        detailAdapter1.PrepareCommand(null, detailAdapter1.DeleteStoredProc, new PopulateParameters(detailAdapter1.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    TAdapter2 detailAdapter2 = new TAdapter2();
                    db.UpdateDataSet(detailDataSet2, String.IsNullOrEmpty(this.detailTableName2) ? detailDataSet2.Tables[0].TableName : this.detailTableName2,
                        detailAdapter2.PrepareCommand(null, detailAdapter2.InsertStoredProc, new PopulateParameters(detailAdapter2.PopulateInsertParameters), isStoredProc),
                        detailAdapter2.PrepareCommand(null, detailAdapter2.UpdateStoredProc, new PopulateParameters(detailAdapter2.PopulateUpdateParameters), isStoredProc),
                        detailAdapter2.PrepareCommand(null, detailAdapter2.DeleteStoredProc, new PopulateParameters(detailAdapter2.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    transaction.Commit();

                    if (removeCache)
                    {
                        if (cacheItemKeys != null && cacheItemKeys.Length > 0)
                        {
                            DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                        }
                        else
                        {
                            string[] cacheKeys = new string[3];
                            cacheKeys[0] = DataAccessUtil.GetCacheKey(commandText);
                            cacheKeys[1] = detailAdapter1.tableName.Length > 0 ? detailAdapter1.tableName + "_1" : detailDataSet1.Tables[0].TableName;
                            cacheKeys[2] = detailAdapter2.tableName.Length > 0 ? detailAdapter2.tableName + "_1" : detailDataSet2.Tables[0].TableName;
                            DataAccessUtil.UpdateCacheDependency(cacheKeys);
                        }
                    }

                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TAdapter1"></typeparam>
        /// <typeparam name="OS1"></typeparam>
        /// <typeparam name="OT1"></typeparam>
        /// <typeparam name="OR1"></typeparam>
        /// <typeparam name="TAdapter2"></typeparam>
        /// <typeparam name="OS2"></typeparam>
        /// <typeparam name="OT2"></typeparam>
        /// <typeparam name="OR2"></typeparam>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="detailDataSet1"></param>
        /// <param name="detailDataSet2"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public virtual bool ExecuteNonQuery<TAdapter1, OS1, OT1, OR1, TAdapter2, OS2, OT2, OR2>(string commandText, IEnumerable<DatabaseParameter> parameters, DataSet detailDataSet1, DataSet detailDataSet2, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
            where TAdapter1 : DataAccessAdapter<OS1, OT1, OR1>, new()
            where TAdapter2 : DataAccessAdapter<OS2, OT2, OR2>, new()
            where OS1 : DataSet, new()
            where OT1 : DataTable, new()
            where OR1 : DataRow
            where OS2 : DataSet, new()
            where OT2 : DataTable, new()
            where OR2 : DataRow
        {
            bool returnValue = false;
            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    db.ExecuteNonQuery(PrepareCommand(null, commandText, parameters, isStoredProc), transaction);

                    TAdapter1 detailAdapter1 = new TAdapter1();
                    db.UpdateDataSet(detailDataSet1, String.IsNullOrEmpty(this.detailTableName) ? detailDataSet1.Tables[0].TableName : this.detailTableName,
                        detailAdapter1.PrepareCommand(null, detailAdapter1.InsertStoredProc, new PopulateParameters(detailAdapter1.PopulateInsertParameters), isStoredProc),
                        detailAdapter1.PrepareCommand(null, detailAdapter1.UpdateStoredProc, new PopulateParameters(detailAdapter1.PopulateUpdateParameters), isStoredProc),
                        detailAdapter1.PrepareCommand(null, detailAdapter1.DeleteStoredProc, new PopulateParameters(detailAdapter1.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    TAdapter2 detailAdapter2 = new TAdapter2();
                    db.UpdateDataSet(detailDataSet2, String.IsNullOrEmpty(this.detailTableName2) ? detailDataSet2.Tables[0].TableName : this.detailTableName2,
                        detailAdapter2.PrepareCommand(null, detailAdapter2.InsertStoredProc, new PopulateParameters(detailAdapter2.PopulateInsertParameters), isStoredProc),
                        detailAdapter2.PrepareCommand(null, detailAdapter2.UpdateStoredProc, new PopulateParameters(detailAdapter2.PopulateUpdateParameters), isStoredProc),
                        detailAdapter2.PrepareCommand(null, detailAdapter2.DeleteStoredProc, new PopulateParameters(detailAdapter2.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    transaction.Commit();

                    if (removeCache)
                    {
                        if (cacheItemKeys != null && cacheItemKeys.Length > 0)
                        {
                            DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                        }
                        else
                        {
                            string[] cacheKeys = new string[3];
                            cacheKeys[0] = DataAccessUtil.GetCacheKey(commandText);
                            cacheKeys[1] = detailAdapter1.tableName.Length > 0 ? detailAdapter1.tableName + "_1" : detailDataSet1.Tables[0].TableName;
                            cacheKeys[2] = detailAdapter2.tableName.Length > 0 ? detailAdapter2.tableName + "_1" : detailDataSet2.Tables[0].TableName;
                            DataAccessUtil.UpdateCacheDependency(cacheKeys);
                        }
                    }

                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }



        #endregion

        #region UpdateDataSet
        /// <summary>
        /// 这里仅仅更新dataSet的TableName表
        /// </summary>
        /// <param name="dataSet"></param>
        /// <returns></returns>
        public int Update(DataSet dataSet, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
        {
            //PopulateParameters insertParameter = new PopulateParameters(PopulateInsertParamters);
            //PopulateParameters updateParameter = new PopulateParameters(PopulateUpdateParamters);
            //PopulateParameters deleteParameter = new PopulateParameters(PopulateDeleteParamters);
            int cnt = 0;
            if (dataSet == null || dataSet.Tables.Count == 0)
            {
                return 0;
            }

            int tableCount = 0;
            foreach (DataTable dt in dataSet.Tables)
            {
                if (dt.Rows.Count == 0)
                {
                    tableCount++;
                }
            }
            if (tableCount == dataSet.Tables.Count) return 0;


            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    tableName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
                    LogWriter.WriteLogEntry(LogType.Operation, this.GetLogInfomation(dataSet, tableName), LogTitle(tableName));

                    insertCommand = PrepareCommand(insertCommand, InsertStoredProc, new PopulateParameters(PopulateInsertParameters), isStoredProc);
                    updateCommand = PrepareCommand(updateCommand, UpdateStoredProc, new PopulateParameters(PopulateUpdateParameters), isStoredProc);
                    deleteCommand = PrepareCommand(deleteCommand, DeleteStoredProc, new PopulateParameters(PopulateDeleteParameters), isStoredProc);
                    cnt = db.UpdateDataSet(dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName,
                        insertCommand,
                        updateCommand,
                        deleteCommand, 
                        transaction, null);
                    transaction.Commit();
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }

            if (removeCache && cacheItemKeys != null)
            {
                DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
            }

            return cnt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataSet"></param>
        /// <param name="dtName"></param>
        /// <returns></returns>
        protected virtual IDictionary<string, object> GetLogInfomation(DataSet dataSet, string dtName)
        {
            //string dtName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
            DataTable dt = dataSet.Tables[dtName];
            IDictionary<string, object> dict = new Dictionary<string, object>();

            if (dt != null && dt.Rows.Count > 0)
            {
                // 如果仅仅是一行数据：往往是更新数据表
                if (dt.Rows.Count == 1)
                {
                    GenerateLog(false, dt.Rows[0], dict);
                    return dict;
                }
                // 如果表里面有多行数据
                foreach (DataRow row in dt.Rows)
                {
                    //for (int i = 0; dt.Columns.Count; i++)
                    //{
                        
                    //}
                    GenerateLog(true, row, dict);
                }
            }
            return dict;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hasPrefix"></param>
        /// <param name="column"></param>
        /// <param name="row"></param>
        /// <param name="dict"></param>
        private void GenerateLog(bool hasPrefix, DataRow row, IDictionary<string, object> dict)
        {
            if (row.RowState == DataRowState.Modified)
            {
                GenerateFieldLog(hasPrefix, row, dict);
            }

            if (row.RowState == DataRowState.Deleted)
            {

                GenerateStatelessFieldLog(hasPrefix, row, dict);
            }

            if (row.RowState == DataRowState.Added)
            {
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataSet"></param>
        /// <param name="dtName"></param>
        /// <returns></returns>
        protected virtual IDictionary<string, object> GetStatelessLogInfomation(DataSet dataSet, string dtName)
        {
            DataTable dt = dataSet.Tables[dtName];
            IDictionary<string, object> dict = new Dictionary<string, object>();

            if (dt != null && dt.Rows.Count > 0)
            {
                if (dt.Rows.Count == 1)
                {
                    GenerateStatelessFieldLog(false, dt.Rows[0], dict);
                    return dict;
                }

                foreach (DataRow row in dt.Rows)
                {
                    GenerateStatelessFieldLog(true, row, dict);
                }
            }
            return dict;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hasPrefix"></param>
        /// <param name="column"></param>
        /// <param name="row"></param>
        /// <param name="dict"></param>
        private void GenerateStatelessFieldLog(bool hasPrefix, DataRow row, IDictionary<string, object> dict)
        {
            string prefix = "";
            if (hasPrefix)
            {
                if (row.Table.Columns.Contains("Name"))
                {
                    prefix = row["Name"].ToString();
                }
                else
                {
                    if (row.Table.PrimaryKey.Length > 0) prefix = row[row.Table.PrimaryKey[0].ColumnName, DataRowVersion.Original].ToString();
                }
            }

            foreach (DataColumn column in row.Table.Columns)
            {
                if (column.Caption != null && column.Caption.Length > 0)
                {
                    if (row[column, DataRowVersion.Original] != DBNull.Value && row[column, DataRowVersion.Original].ToString().Length > 0)
                    {
                        dict.Add(hasPrefix ? prefix + ":" + column.Caption : column.Caption, row[column, DataRowVersion.Original].ToString());
                    }
                }
                else
                {
                    if (row[column, DataRowVersion.Original] != DBNull.Value && row[column, DataRowVersion.Original].ToString().Length > 0)
                    {
                        dict.Add(hasPrefix ? prefix + ":" + column.ColumnName : column.ColumnName, row[column, DataRowVersion.Original].ToString());
                    }
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hasPrefix"></param>
        /// <param name="column"></param>
        /// <param name="row"></param>
        /// <param name="dict"></param>
        private void GenerateFieldLog(bool hasPrefix, DataRow row, IDictionary<string, object> dict)
        {
            string prefix = "";
            if (hasPrefix)
            {
                if (row.Table.Columns.Contains("Name"))
                {
                    prefix = row["Name"].ToString();
                }
                else 
                {
                    if (row.Table.PrimaryKey.Length > 0)
                    {
                        prefix = row[row.Table.PrimaryKey[0].ColumnName, DataRowVersion.Original].ToString();
                    }
                    else
                    {
                        prefix = row[0, DataRowVersion.Original].ToString();
                    }
                }
            }

            foreach (DataColumn column in row.Table.Columns)
            {
                if (column.Caption != null && column.Caption.Length > 0)
                {
                    if (dict.Keys.Contains(hasPrefix ? prefix + ":" + column.Caption : column.Caption))
                    {
                        prefix += "1";
                    }
                    if ((row[column, DataRowVersion.Original] != DBNull.Value || row[column, DataRowVersion.Current] != DBNull.Value) && !row[column, DataRowVersion.Original].Equals(row[column, DataRowVersion.Current]))
                    {
                        dict.Add(hasPrefix ? prefix + ":" + column.Caption : column.Caption, row[column, DataRowVersion.Original].ToString() + " => " + row[column, DataRowVersion.Current].ToString());
                    }

                }
                else
                {
                    if (dict.Keys.Contains(hasPrefix ? prefix + ":" + column.ColumnName : column.ColumnName))
                    {
                        prefix += "1";
                    }
                    if ((row[column, DataRowVersion.Original] != DBNull.Value || row[column, DataRowVersion.Current] != DBNull.Value) && !row[column, DataRowVersion.Original].Equals(row[column, DataRowVersion.Current]))
                    {
                        dict.Add(hasPrefix ? prefix + ":" + column.ColumnName : column.ColumnName, row[column, DataRowVersion.Original].ToString() + " => " + row[column, DataRowVersion.Current].ToString());
                    }
                }
            }
            
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataSet"></param>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public int Update(DataSet dataSet, string commandText, IEnumerable<DatabaseParameter> parameters, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
        {
            //PopulateParameters insertParameter = new PopulateParameters(PopulateInsertParamters);
            //PopulateParameters updateParameter = new PopulateParameters(PopulateUpdateParamters);
            //PopulateParameters deleteParameter = new PopulateParameters(PopulateDeleteParamters);
            if (dataSet == null || dataSet.Tables.Count == 0) return 0;
            tableName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
            LogWriter.WriteLogEntry(LogType.Operation, this.GetLogInfomation(dataSet, tableName), LogTitle(tableName));

            int cnt = db.UpdateDataSet(dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName,
                null,
                PrepareCommandFromDataSet(commandText, parameters, isStoredProc),
                null,
                UpdateBehavior.Standard);

            if (removeCache && cacheItemKeys != null)
            {
                DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
            }
            return cnt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataSet"></param>
        /// <param name="transaction"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public int Update(DataSet dataSet, DbTransaction transaction, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
        {
            //PopulateParameters insertParameter = new PopulateParameters(PopulateInsertParamters);
            //PopulateParameters updateParameter = new PopulateParameters(PopulateUpdateParamters);
            //PopulateParameters deleteParameter = new PopulateParameters(PopulateDeleteParamters);
            if (dataSet == null || dataSet.Tables.Count == 0) return 0;
            tableName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
            LogWriter.WriteLogEntry(LogType.Operation, this.GetLogInfomation(dataSet, tableName), LogTitle(tableName));

            int cnt = db.UpdateDataSet(dataSet, this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName,
                PrepareCommand(insertCommand, InsertStoredProc, new PopulateParameters(PopulateInsertParameters), isStoredProc),
                PrepareCommand(updateCommand, UpdateStoredProc, new PopulateParameters(PopulateUpdateParameters), isStoredProc),
                PrepareCommand(deleteCommand, DeleteStoredProc, new PopulateParameters(PopulateDeleteParameters), isStoredProc),transaction, null);

            if (removeCache && cacheItemKeys != null)
            {
                DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
            }
            return cnt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataSet"></param>
        /// <returns></returns>
        public int Update(DataSet dataSet)
        {
            return this.Update(dataSet, true, true, new string[] { UpdateStoredProc });//dataSet.Tables[0].TableName
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TDataAccessAdapter"></typeparam>
        /// <typeparam name="OS"></typeparam>
        /// <typeparam name="OT"></typeparam>
        /// <typeparam name="OR"></typeparam>
        /// <param name="dataSet"></param>
        /// <param name="detailDataSet"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public bool UpdateSelfAndDetail<TDataAccessAdapter, OS, OT, OR>(DataSet dataSet, DataSet detailDataSet, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
            where TDataAccessAdapter : DataAccessAdapter<OS, OT, OR>, new()
            where OS : DataSet, new()
            where OT : DataTable, new()
            where OR : DataRow
        {
            bool returnValue = false;
            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    tableName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
                    LogWriter.WriteLogEntry(LogType.Operation, this.GetLogInfomation(dataSet, tableName), LogTitle(tableName));

                    db.UpdateDataSet(dataSet, tableName,
                        this.PrepareCommand(insertCommand, InsertStoredProc, new PopulateParameters(PopulateInsertParameters), isStoredProc),
                        this.PrepareCommand(updateCommand, UpdateStoredProc, new PopulateParameters(PopulateUpdateParameters), isStoredProc),
                        this.PrepareCommand(deleteCommand, DeleteStoredProc, new PopulateParameters(PopulateDeleteParameters), isStoredProc),
                        transaction, null);
                    TDataAccessAdapter detailAdapter = new TDataAccessAdapter();


                    detailTableName =  String.IsNullOrEmpty(this.detailTableName) ? detailDataSet.Tables[0].TableName : this.detailTableName;
                    LogWriter.WriteLogEntry(LogType.Operation, detailAdapter.GetLogInfomation(detailDataSet, detailTableName), LogTitle(detailTableName) );

                    db.UpdateDataSet(detailDataSet, detailTableName,
                        detailAdapter.PrepareCommand(null, detailAdapter.InsertStoredProc, new PopulateParameters(detailAdapter.PopulateInsertParameters), isStoredProc),
                        detailAdapter.PrepareCommand(null, detailAdapter.UpdateStoredProc, new PopulateParameters(detailAdapter.PopulateUpdateParameters), isStoredProc),
                        detailAdapter.PrepareCommand(null, detailAdapter.DeleteStoredProc, new PopulateParameters(detailAdapter.PopulateDeleteParameters), isStoredProc),
                        transaction, null);
                    transaction.Commit();

                    if (removeCache && cacheItemKeys != null)
                    {
                        DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                    }
                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TDataAccessAdapter"></typeparam>
        /// <typeparam name="OS"></typeparam>
        /// <typeparam name="OT"></typeparam>
        /// <typeparam name="OR"></typeparam>
        /// <param name="dataSet"></param>
        /// <param name="masterDataSet"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public bool UpdateSelfAndMaster<TDataAccessAdapter, OS, OT, OR>(DataSet dataSet, DataSet masterDataSet, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
            where TDataAccessAdapter : DataAccessAdapter<OS, OT, OR>, new()
            where OS : DataSet, new()
            where OT : DataTable, new()
            where OR : DataRow
        {
            bool returnValue = false;
            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    TDataAccessAdapter masterAdapter = new TDataAccessAdapter();

                    masterTableName = String.IsNullOrEmpty(this.masterTableName) ? masterDataSet.Tables[0].TableName : this.masterTableName;
                    LogWriter.WriteLogEntry(LogType.Operation, masterAdapter.GetLogInfomation(masterDataSet, masterTableName), LogTitle(masterTableName) );

                    db.UpdateDataSet(masterDataSet, masterTableName,
                        masterAdapter.PrepareCommand(null, masterAdapter.InsertStoredProc, new PopulateParameters(masterAdapter.PopulateInsertParameters), isStoredProc),
                        masterAdapter.PrepareCommand(null, masterAdapter.UpdateStoredProc, new PopulateParameters(masterAdapter.PopulateUpdateParameters), isStoredProc),
                        masterAdapter.PrepareCommand(null, masterAdapter.DeleteStoredProc, new PopulateParameters(masterAdapter.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    tableName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
                    LogWriter.WriteLogEntry(LogType.Operation, this.GetLogInfomation(dataSet, tableName), LogTitle(tableName)  );

                    db.UpdateDataSet(dataSet, tableName,
                        this.PrepareCommand(insertCommand, this.InsertStoredProc, new PopulateParameters(this.PopulateInsertParameters), isStoredProc),
                        this.PrepareCommand(updateCommand, this.UpdateStoredProc, new PopulateParameters(this.PopulateUpdateParameters), isStoredProc),
                        this.PrepareCommand(deleteCommand, this.DeleteStoredProc, new PopulateParameters(this.PopulateDeleteParameters), isStoredProc),
                        transaction, null);
                    
                    transaction.Commit();

                    if (removeCache && cacheItemKeys != null)
                    {
                        DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                    }
                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TDataAccessAdapter1"></typeparam>
        /// <typeparam name="OS1"></typeparam>
        /// <typeparam name="OT1"></typeparam>
        /// <typeparam name="OR1"></typeparam>
        /// <typeparam name="TDataAccessAdapter2"></typeparam>
        /// <typeparam name="OS2"></typeparam>
        /// <typeparam name="OT2"></typeparam>
        /// <typeparam name="OR2"></typeparam>
        /// <param name="dataSet"></param>
        /// <param name="detailDataSet1"></param>
        /// <param name="detailDataSet2"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <returns></returns>
        public bool UpdateSelfAndDetail<TDataAccessAdapter1, OS1, OT1, OR1, TDataAccessAdapter2, OS2, OT2, OR2>(DataSet dataSet, DataSet detailDataSet1, DataSet detailDataSet2, bool isStoredProc, bool removeCache, string[] cacheItemKeys)
            where TDataAccessAdapter1 : DataAccessAdapter<OS1, OT1, OR1>, new()
            where TDataAccessAdapter2 : DataAccessAdapter<OS2, OT2, OR2>, new()
            where OS1 : DataSet, new()
            where OT1 : DataTable, new()
            where OR1: DataRow
            where OS2 : DataSet, new()
            where OT2 : DataTable, new()
            where OR2 : DataRow
        {
            bool returnValue = false;
            using (DbConnection connection = db.CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    tableName = this.tableName.Length > 0 ? this.tableName : dataSet.Tables[0].TableName;
                    LogWriter.WriteLogEntry(LogType.Operation, this.GetLogInfomation(dataSet, tableName), LogTitle(tableName));

                    db.UpdateDataSet(dataSet, this.tableName,
                        this.PrepareCommand(insertCommand, InsertStoredProc, new PopulateParameters(PopulateInsertParameters), isStoredProc),
                        this.PrepareCommand(updateCommand, UpdateStoredProc, new PopulateParameters(PopulateUpdateParameters), isStoredProc),
                        this.PrepareCommand(deleteCommand, DeleteStoredProc, new PopulateParameters(PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    TDataAccessAdapter1 detailAdapter1 = new TDataAccessAdapter1();
                    detailTableName = String.IsNullOrEmpty(this.detailTableName) ? detailDataSet1.Tables[0].TableName : this.detailTableName;
                    LogWriter.WriteLogEntry(LogType.Operation, detailAdapter1.GetLogInfomation(detailDataSet1, detailTableName), LogTitle(detailTableName) );

                    db.UpdateDataSet(detailDataSet1, this.detailTableName,
                        detailAdapter1.PrepareCommand(null, detailAdapter1.InsertStoredProc, new PopulateParameters(detailAdapter1.PopulateInsertParameters), isStoredProc),
                        detailAdapter1.PrepareCommand(null, detailAdapter1.UpdateStoredProc, new PopulateParameters(detailAdapter1.PopulateUpdateParameters), isStoredProc),
                        detailAdapter1.PrepareCommand(null, detailAdapter1.DeleteStoredProc, new PopulateParameters(detailAdapter1.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    TDataAccessAdapter2 detailAdapter2 = new TDataAccessAdapter2();
                    detailTableName2 = String.IsNullOrEmpty(this.detailTableName2) ? detailDataSet2.Tables[0].TableName : this.detailTableName2;
                    LogWriter.WriteLogEntry(LogType.Operation, detailAdapter2.GetLogInfomation(detailDataSet2, detailTableName2), LogTitle(detailTableName2) );

                    db.UpdateDataSet(detailDataSet2, this.detailTableName2,
                        detailAdapter2.PrepareCommand(null, detailAdapter2.InsertStoredProc, new PopulateParameters(detailAdapter2.PopulateInsertParameters), isStoredProc),
                        detailAdapter2.PrepareCommand(null, detailAdapter2.UpdateStoredProc, new PopulateParameters(detailAdapter2.PopulateUpdateParameters), isStoredProc),
                        detailAdapter2.PrepareCommand(null, detailAdapter2.DeleteStoredProc, new PopulateParameters(detailAdapter2.PopulateDeleteParameters), isStoredProc),
                        transaction, null);

                    transaction.Commit();

                    if (removeCache && cacheItemKeys != null)
                    {
                        DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                    }
                    returnValue = true;
                }
                catch //(Exception ex)
                {
                    transaction.Rollback();
                    //if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
                    throw;
                }
                finally
                {
                    connection.Close();
                }
            }
            return returnValue;
        }

        #endregion

        #region Delete

        public virtual int Delete(string id, bool isStoredProc, bool removeCache, string[] cacheItemKeys, bool logical)
        {
            if (String.IsNullOrEmpty(DeleteStoredProc))
            {
                throw new ArgumentNullException("DeleteStoredProc不能为空, 请Adapter中设置DeleteStoredProc的值.");
            }

            #region 写日志
            R row;
            if (!String.IsNullOrEmpty(GetByIdStoredProc))
            {
                row = GetById(id, isStoredProc);
            }
            else
            {
                row = GetByIdFromCache(id);
            }

            if (row == null) return 0;

            tableName = this.tableName.Length > 0 ? this.tableName : row.Table.DataSet.Tables[0].TableName;
            IDictionary<string, object> dict = new Dictionary<string, object>();
            GenerateStatelessFieldLog(false, row, dict);
            if (logical)
            {
                LogWriter.WriteLogEntry(LogType.Operation, dict, LogTitle(tableName, 2));
            }
            else
            {
                LogWriter.WriteLogEntry(LogType.Operation, dict, LogTitle(tableName, 2));
                //LogWriter.WriteLogEntry(LogType.Operation, GetStatelessLogInfomation(row.Table.DataSet, tableName), "表：" + tableName + "物理删除一行数据");
            }
            #endregion

            int cnt = db.ExecuteNonQuery(PrepareDeleteCommand(deleteCommand, logical ? LogicalDeleteStoredProc : DeleteStoredProc, id, new PopulateStringIdParameters(PopulateStringIdParameters), isStoredProc));
            if (removeCache && cacheItemKeys != null)
            {
                DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
            }
            return cnt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <param name="logical"></param>
        /// <returns></returns>
        public virtual int Delete(Guid id, bool isStoredProc, bool removeCache, string[] cacheItemKeys, bool logical)
        {
            if (String.IsNullOrEmpty(DeleteStoredProc))
            {
                throw new ArgumentNullException("DeleteStoredProc不能为空, 请Adapter中设置DeleteStoredProc的值.");
            }

            #region 写日志
            R row;
            if (!String.IsNullOrEmpty(GetByIdStoredProc))
            {
                row = GetById(id, isStoredProc);
            }
            else
            {
                row = GetByIdFromCache(id);
            }
            
            if (row == null) return 0;

            tableName = this.tableName.Length > 0 ? this.tableName : row.Table.DataSet.Tables[0].TableName;
            IDictionary<string, object> dict = new Dictionary<string, object>();
            GenerateStatelessFieldLog(false, row, dict);
            if (logical)
            {
                LogWriter.WriteLogEntry(LogType.Operation, dict, LogTitle(tableName, 2) );
            }
            else
            {
                LogWriter.WriteLogEntry(LogType.Operation, dict, LogTitle(tableName, 2));
                //LogWriter.WriteLogEntry(LogType.Operation, GetStatelessLogInfomation(row.Table.DataSet, tableName), "表：" + tableName + "物理删除一行数据");
            }
            #endregion

            int cnt = 0;
            try
            {

                cnt = db.ExecuteNonQuery(PrepareDeleteCommand(deleteCommand, logical ? LogicalDeleteStoredProc : DeleteStoredProc, id, new PopulateIdParameters(PopulateIdParameters), isStoredProc));
                if (removeCache && cacheItemKeys != null)
                {
                    DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return cnt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="isStoredProc"></param>
        /// <param name="removeCache"></param>
        /// <param name="cacheItemKeys"></param>
        /// <param name="logical"></param>
        /// <returns></returns>
        public virtual int Delete(int id, bool isStoredProc, bool removeCache, string[] cacheItemKeys, bool logical)
        {
            //bool isNumber = CommonUtil.IsNumber(id);
            if (String.IsNullOrEmpty(DeleteStoredProc))
            {
                throw new ArgumentNullException("DeleteStoredProc不能为空, 请Adapter中设置DeleteStoredProc的值.");
            }

            #region 写日志
            R row = GetById(id, isStoredProc);
            if (row == null) return 0;

            tableName = this.tableName.Length > 0 ? this.tableName : row.Table.DataSet.Tables[0].TableName;
            IDictionary<string, object> dict = new Dictionary<string, object>();
            GenerateStatelessFieldLog(false, row, dict);

            if (logical)
            {
                LogWriter.WriteLogEntry(LogType.Operation, dict, LogTitle(tableName, 2));
            }
            else
            {
                LogWriter.WriteLogEntry(LogType.Operation, dict, LogTitle(tableName, 2));
            }
            #endregion

            List<DatabaseParameter> ps = new List<DatabaseParameter>();
            ps.Add(new DatabaseParameter("Id", DbType.Int32, id));
            //if (isNumber)
            //{
            //    ps.Add(new DatabaseParameter("Id", DbType.Int32, Int32.Parse(id)));
            //}
            //else
            //{
            //    ps.Add(new DatabaseParameter("Id", DbType.AnsiString, id));
            //}


            int cnt = db.ExecuteNonQuery(PrepareDeleteCommand(deleteCommand, logical ? LogicalDeleteStoredProc : DeleteStoredProc, id, ps, isStoredProc));
            if (removeCache && cacheItemKeys != null)
            {
                DataAccessUtil.UpdateCacheDependency(cacheItemKeys);
            }
            return cnt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="logical"></param>
        /// <returns></returns>
        public virtual int Delete(Guid id, bool logical)
        {
            if (String.IsNullOrEmpty(DeleteStoredProc))
            {
                throw new ArgumentNullException("LogicalDeleteStoredProc或者DeleteStoredProc不能为空, 请Adapter中设置LogicalDeleteStoredProc或者DeleteStoredProc的值.");
            }
            return this.Delete(id, true, true, new string[] { logical ? LogicalDeleteStoredProc : DeleteStoredProc }, logical);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual int Delete(Guid id)
        {
            if (String.IsNullOrEmpty(DeleteStoredProc))
            {
                throw new ArgumentNullException("DeleteStoredProc不能为空, 请Adapter中设置DeleteStoredProc的值.");
            }
            return this.Delete(id, true, true, new string[] { DeleteStoredProc }, false);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        /// <param name="criteria"></param>
        /// <param name="isStoredProc"></param>
        /// <returns></returns>
        public virtual int Delete(string commandText, string criteria, bool isStoredProc)
        {
            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            parameters.Add(new DatabaseParameter("WhereCondition", DbType.String, criteria));

            return this.ExecuteNonQuery(commandText, parameters, isStoredProc);
        }


        private static string LogTitle(string tableName, int identifier)
        {
            string s = "";
            if(identifier == 0)
            {
                s += "数据库表或者视图：" + tableName + "更新";
            }
            else
            {
                s += "表：" + tableName + "物理删除一行数据";
            }
            HybridDictionary ctx = AppContext.GetGlobalContext();
            if (ctx != null && ctx.Count > 0)
            {
                s += "," + (string)ctx["ActualUserName"];
                s += "," + (string)ctx["UserHostName"];
            }
            return s;
        }

        private static string LogTitle(string tableName)
        {
            return LogTitle(tableName, 0);
        }

        #endregion

        #region abstract & virtual method
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        protected abstract void PopulateInsertParameters(Database db, DbCommand cmd);
        //protected abstract void PopulateUpdateParamters(Database db, DbCommand cmd);
        //protected abstract void PopulateDeleteParamters(Database db, DbCommand cmd);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="orderByExpression"></param>
        /// <returns></returns>
        protected virtual IEnumerable<DatabaseParameter> GetSearchParameters(string orderByExpression)
        {
            string where = "1 = 1 ";
            where += GetCriteria();
            DataAccessUtil.TrimWhereClause(ref where);
            if (where.Trim() == "1 = 1")
            {
                where = "";
            }
            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            if (where != null && where.Length > 0)
            {
                parameters.Add(new  DatabaseParameter("WhereCondition", DbType.String, where));
            }
            if (!string.IsNullOrEmpty(orderByExpression))
            {
                parameters.Add(new  DatabaseParameter("OrderByExpression", DbType.String, orderByExpression));

            }

            return parameters;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="whereClause"></param>
        /// <param name="orderByExpression"></param>
        /// <returns></returns>
        protected virtual IEnumerable<DatabaseParameter> GetSearchParameters(string whereClause, string orderByExpression)
        {
            string where = "";
            if (!string.IsNullOrEmpty(whereClause))
            {
                //where += this.GenerateWhereClause(domainObject);
                where += whereClause;
                DataAccessUtil.TrimWhereClause(ref where);
            }
            //else
            //{
            //    where = "1 = 1";
            //}
            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            if (where != null && where.Length > 0)
            {
                parameters.Add(new  DatabaseParameter("WhereCondition", DbType.String, where));
            }
            if (!string.IsNullOrEmpty(orderByExpression))
            {
                parameters.Add(new  DatabaseParameter("OrderByExpression", DbType.String, orderByExpression));

            }

            return parameters;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected virtual string GetCriteria()
        {
            //throw new Exception("The method or operation is not implemented.");
            return "";
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        /// <param name="id"></param>
        protected virtual void PopulateIdParameters(Database db, DbCommand cmd, Guid id)
        {
            db.AddInParameter(cmd, "Id", DbType.Guid, id);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        /// <param name="id"></param>
        protected virtual void PopulateStringIdParameters(Database db, DbCommand cmd, string id)
        {
            db.AddInParameter(cmd, "Id", DbType.AnsiString, id);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        /// <param name="id"></param>
        protected virtual void PopulateInt32IdParameters(Database db, DbCommand cmd, Int32 id)
        {
            db.AddInParameter(cmd, "Id", DbType.Int32, id);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        protected virtual void PopulateIdParameters(Database db, DbCommand cmd)
        {
            db.AddInParameter(cmd, "Id", DbType.Guid, "Id", DataRowVersion.Current);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        protected virtual void PopulateDeleteParameters(Database db, DbCommand cmd)
        {
            //PopulateIdParameters(db, cmd);
            db.AddInParameter(cmd, "Id", DbType.Guid, "Id", DataRowVersion.Original);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="cmd"></param>
        protected virtual void PopulateUpdateParameters(Database db, DbCommand cmd)
        {
            PopulateInsertParameters(db, cmd);
        }

        #endregion
    }
}
